package com.leenmvc.core.utils.socket;

import cn.hutool.core.io.BufferUtil;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.socket.nio.NioServer;
import com.alibaba.fastjson.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.SocketChannel;
import java.util.HashMap;
import java.util.Map;

/**
 * 这个类是NIOServer的服务端
 */
public class SocketServer implements Runnable {

    private final static Logger logger = LoggerFactory.getLogger(SocketServer.class);

    /**
     * 广播频道
     */
    private Map<String, SocketChannel> channels = new HashMap<>();

    public static final String START_STR = ">>>>>>>>>>>>";

    public static final String END_STR = "<<<<<<<<<<<<";

    public static final String SHUTDOWN_THEME = "shutdown_client";

    private Map<SocketChannel, StringBuffer> receiveContents = new HashMap<>();

    /**
     * 通信端口
     */
    private int port;

    public SocketServer(int port) {
        this.port = port;
    }

    public void run() {
        NioServer server = new NioServer(port);
        server.setChannelHandler((sc) -> {
            ByteBuffer readBuffer = ByteBuffer.allocate(1024);
            try {
                //从channel读数据到缓冲区
                int readBytes = sc.read(readBuffer);
                if (readBytes > 0) {
                    // Flips this buffer.  The limit is set to the current position and then
                    // the position is set to zero，就是表示要从起始位置开始读取数据
                    readBuffer.flip();
                    //eturns the number of elements between the current position and the  limit.
                    // 要读取的字节长度
                    byte[] bytes = new byte[readBuffer.remaining()];
                    //将缓冲区的数据读到bytes数组
                    readBuffer.get(bytes);
                    String body = StrUtil.utf8Str(bytes);
                    if(receiveContents.get(sc) == null) {
                        receiveContents.put(sc, new StringBuffer());
                    }
                    StringBuffer append = receiveContents.get(sc).append(body);
                    // 拆包
                    dataHandler(append);
                } else if (readBytes < 0) {
                    IoUtil.close(sc);
                }
            } catch (IOException e) {
                logger.error(e.getMessage(), e);
                throw new IORuntimeException(e);
            } catch (Exception e) {
                logger.error(e.getMessage(), e);
            }
        });
        server.listen();
    }

    /**
     * 对属于该通道的内容进行一次解析
     *
     */
    private void dataHandler(StringBuffer data) {
        int index = 0;
        while(true) {
            int start = index = data.indexOf(START_STR, index);
            if(start == -1) { // 终止条件为找不到指定占位标识符
                data.setLength(0);
                break;
            }
            int end = index = data.indexOf(END_STR, index);
            if(end == -1) { // 断包了
                int newLength = data.length() - start; // 新长度
                CharSequence sequence = data.subSequence(start, start + newLength);
                data.setLength(newLength);
                data.replace(0, newLength, sequence.toString());
                break;
            }

            CharSequence charSequence = data.subSequence(start + START_STR.length(), end);
            String socketMessageStr = charSequence.toString();
            SocketMessage socketMessage = JSONObject.parseObject(socketMessageStr, SocketMessage.class);
            sendHandler(socketMessage);
        }
    }


    /**
     * 分发处理
     *
     * @param socketMessage
     */
    private void sendHandler(SocketMessage socketMessage) {
        switch (socketMessage.getType()) {
            case SEND_TO_ALL:
                broadcast(socketMessage);
                break;
            case SEND_TO_MANY:
                sendMany(socketMessage);
                break;
            case SEND_TO_ONE:
                sendToOne(socketMessage);
                break;
            case NO_SEND:
                break;
            default:
        }
    }

    public void doWrite(SocketChannel channel, String response) {
        // 将缓冲数据写入渠道，返回给客户端
        try {
            channel.write(BufferUtil.createUtf8(response));
        } catch (ClosedChannelException e) {
            logger.info("将要把下线的channel移除: {}", channel);
            channels.remove(channel);
        } catch (IOException e) {
            logger.error(e.getMessage(), e);
        }
    }

    /**
     * 广播所有
     */
    public void broadcast(SocketMessage socketMessage) {
        for (String channel : channels.keySet()) {
            SocketChannel channel1 = channels.get(channel);
            try {
                socketMessage.setFrom(channel1.getRemoteAddress().toString());
                doWrite(channel1, JSONObject.toJSONString(socketMessage));
            } catch (IOException e) {
                logger.error(e.getMessage(), e);
            }
        }
    }

    public void sendMany(SocketMessage socketMessage) {
        for (String address : socketMessage.getTos()) {
            SocketChannel channel1 = channels.get(address);
            try {
                socketMessage.setFrom(channel1.getRemoteAddress().toString());
                doWrite(channel1, JSONObject.toJSONString(socketMessage));
            } catch (IOException e) {
                logger.error(e.getMessage(), e);
            }
        }
    }

    public void sendToOne(SocketMessage socketMessage) {
        SocketChannel channel = channels.get(socketMessage.getTo());
        if(channel != null) {
            doWrite(channel, JSONObject.toJSONString(socketMessage));
        }
    }

}